#include "widget.h"
#include "ui_widget.h"

#include <QtCore>

#include <SDL/SDL.h>
#include <stdio.h>
#include <stdarg.h>
#include <libavformat/metadata.h>

#include "t_my_av_utils.h"
#include "t_box_player.h"
#include "t_box_input.h"
#include "t_box_input_http.h"

#include "t_youtube.h"

void my_av_log_callback(void* ptr, int level, const char* fmt, va_list vl)
{
    static QByteArray s_buffer;
    QString v_print;
    v_print.vsprintf(fmt, vl);
    s_buffer.append(v_print.toLatin1());
    QBuffer v_buffer(&s_buffer);
    v_buffer.open(QIODevice::ReadOnly);
    if(v_buffer.canReadLine())
    {
        QString v_line = v_buffer.readLine();
        v_line.replace(QRegExp("(\\r|\\n)"), "");
        qDebug() << "[av_log]" << v_line;
        s_buffer = v_buffer.readAll();
    }
    v_buffer.close();
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , m_player_ctx(0)
    , m_media_input(0)
    , m_audioOutput(0)

{
    ui->setupUi(this);

    screenWidget = new T_ScreenWidget(this);
    screenWidget->setObjectName(QString::fromUtf8("screenWidget"));
    ui->verticalLayout->addWidget(screenWidget);

    // http://www.youtube.com/watch?v=TqYen5ybVrg
    T_Youtube v_youtube(&m_nam);
    QMap<int, QUrl> v_fmt_map;
    //QString v_token = v_youtube.getVideoStreamMap("TqYen5ybVrg", v_fmt_map); //Soraya - Self Control
    //QString v_token = v_youtube.getVideoStreamMap("IwljUPa05Yo", v_fmt_map); //Tommy february6 - Bloomin'!
    QString v_token = v_youtube.getVideoStreamMap("hKL_EFvOLD8", v_fmt_map); //「涙サプライズ！」を踊ってみた
    qDebug() << "[v_token]" << v_token;
    qDebug() << "[v_fmt_map]" << v_fmt_map;
    qDebug() << "[v_fmt_map.value(34)]" << v_fmt_map.value(34);

    NetworkBatch v_nbat(&m_nam);

    QVariant v_loc = v_fmt_map.value(34);
    QNetworkReply *v_reply = NULL;
    while(v_loc.isValid())
    {
        delete v_reply;
        qDebug() << "[v_loc]" << v_loc;
        v_reply = v_nbat.head(v_loc.toUrl());
        v_loc = v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        qDebug() << v_reply->header(QNetworkRequest::ContentTypeHeader);
        qDebug() << v_reply->header(QNetworkRequest::ContentLengthHeader);
        qDebug() << v_reply->header(QNetworkRequest::LocationHeader);
        foreach(QByteArray v_first, v_reply->rawHeaderList())
        {
            qDebug() << v_first << v_reply->rawHeader(v_first);
        }
    }
#if 0x0
    QNetworkReply *v_reply = v_nbat.head(v_fmt_map.value(34));
    qDebug() << v_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    qDebug() << v_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
    qDebug() << v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    qDebug() << v_reply->header(QNetworkRequest::ContentTypeHeader);
    qDebug() << v_reply->header(QNetworkRequest::ContentLengthHeader);
    qDebug() << v_reply->header(QNetworkRequest::LocationHeader);
    foreach(QByteArray v_first, v_reply->rawHeaderList())
    {
        qDebug() << v_first << v_reply->rawHeader(v_first);
    }
#endif

    QVariant v_cont_header = v_reply->header(QNetworkRequest::ContentTypeHeader);
    if(!v_cont_header.isValid())
    {
        qDebug() << "(!v_cont_header.isValid())";
        return;
    }

    qDebug() << v_cont_header;

    if(!v_cont_header.toString().startsWith("video/"))
    {
        qDebug() << "(!v_cont_header.toString().startsWith(\"video/\"))";
        return;
    }

    qDebug() << "ok";


    //filename="C:/6tQzHr5mgbc_34.flv";
    //filename="C:/EWeSEyvAJpc_34.flv";
    //filename="C:/Soraya_-_Self_Control.mp4";
    filename="C:/temp.flv";
    qDebug() << filename;

    av_register_all();

    av_log_set_callback(my_av_log_callback);

#if 0x0
    m_media_input = new T_Box_Input_File(filename);
#else
    QNetworkRequest v_video_request(v_fmt_map.value(34));
    QNetworkReply *v_video_reply = m_nam.get(v_video_request);
    m_media_input = new T_Box_Input_Http(v_video_reply);
#endif



    if(!m_media_input->isValid())
    {
        qDebug() << "(!v_file_io->isValid())" << filename;
        return;
    }

    m_player_ctx = m_media_input->getAVFormatContext2();
    if(!m_player_ctx)
    {
        qDebug() << "Can't open video";
        return;
    }
    ////m_format_ctx = m_box_player_ctx->m_format_ctx;

    if(!m_player_ctx->m_audio.isValid)
    {
        qDebug() << "(!m_player_ctx->m_audio.isValid)";
        return;
    }

    if(!m_player_ctx->m_video.isValid)
    {
        qDebug() << "(!m_player_ctx->m_video.isValid)";
        return;
    }

#if 0x0
    qDebug() << "[before av_find_stream_info()]";
    if(av_find_stream_info(m_format_ctx)<0)
    {
        qDebug() << "Can't find stream info";
    }
#endif

    qDebug() << "[before dump_format()]";
    dump_format(m_player_ctx->m_format_ctx, 0, filename.toLocal8Bit().constData(), false);
    qDebug() << "[after dump_format()]";


    QString v_print;
    //[Format Name]
    qDebug() << v_print.sprintf(
            "formatCtx->iformat->long_name=%s (->name=%s)"
            , m_player_ctx->m_format_ctx->iformat->long_name
            , m_player_ctx->m_format_ctx->iformat->name
            );
    //[Meta Data]
    AVMetadata *m = m_player_ctx->m_format_ctx->metadata;
    if(m && !(m->count == 1 && av_metadata_get(m, "language", NULL, 0))){
        AVMetadataTag *tag=NULL;
        av_log(NULL, AV_LOG_INFO, "%sMetadata:\n", "  ");
        while((tag=av_metadata_get(m, "", tag, AV_METADATA_IGNORE_SUFFIX))) {
            if(strcmp("language", tag->key))
                av_log(NULL, AV_LOG_INFO, "%s  %-16s: %s\n", "  ", tag->key, tag->value);
        }
    }

    //[nb_programs]
    qDebug() << QString("formatCtx->nb_programs=%1").arg(m_player_ctx->m_format_ctx->nb_programs);

    //[nb_chapters]
    qDebug() << QString("formatCtx->nb_chapters=%1").arg(m_player_ctx->m_format_ctx->nb_chapters);

    //[nb_streams]
    qDebug() << QString("formatCtx->nb_streams=%1").arg(m_player_ctx->m_format_ctx->nb_streams);

    for(int i=0;i<(int)m_player_ctx->m_format_ctx->nb_streams;i++)
    {
        my_dump_stream_format(m_player_ctx->m_format_ctx, i, 0, false);
        AVStream *st = m_player_ctx->m_format_ctx->streams[i];
        int g = av_gcd(st->time_base.num, st->time_base.den);
        av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d\n", st->codec_info_nb_frames, st->time_base.num/g, st->time_base.den/g);
        char buf[256];
        my_avcodec_string(buf, sizeof(buf), st->codec, false);
        qDebug() << buf;
    }

    //[Video/SDL]
    //SDL_putenv("SDL_VIDEODRIVER=windib");
    //SDL_putenv("SDL_VIDEODRIVER=directx");
    //SDL_putenv(QString("SDL_WINDOWID=%1").arg((quint32)this->screenWidget->winId()).toLatin1().constData());
    SDL_Init(SDL_INIT_VIDEO);
    window_id = SDL_CreateWindowFrom((void *)this->screenWidget->winId());
    Uint32 flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
    screenWidget->screen = SDL_SetVideoModeEx(
            screenWidget->width(), //640,
            screenWidget->height(), //360,
            0,
            flags,
            window_id);
    if( ! screenWidget->screen )
    {
        qDebug() << "( ! screenWidget->screen )";
        return;
    }

    char namebuf[256+1];
    qDebug() << "[SDL_VideoDriverName()]" << SDL_VideoDriverName(namebuf, sizeof(namebuf)-1);

    qDebug() << "[audioCodec->long_name]" << m_player_ctx->m_audio.codec->long_name;
    qDebug() << "[audioCtx->sample_fmt]" << m_player_ctx->m_audio.codec_ctx->sample_fmt;
    qDebug() << "[audioCtx->sample_rate]" << m_player_ctx->m_audio.codec_ctx->sample_rate;

    //[Audio/Qt]
    m_format.setFrequency(m_player_ctx->m_audio.codec_ctx->sample_rate);
    m_format.setChannels(m_player_ctx->m_audio.codec_ctx->request_channels);
    m_format.setSampleSize(16);
    m_format.setCodec("audio/pcm");
    m_format.setByteOrder(QAudioFormat::LittleEndian);
    m_format.setSampleType(QAudioFormat::SignedInt);

    m_device = QAudioDeviceInfo::defaultOutputDevice();
    if(!m_device.isFormatSupported(m_format))
    {
        qDebug() << "(!m_device.isFormatSupported(m_format))";
        return;
    }

    //double v_time_base = 1.0 * v_stream->time_base.num / v_stream->time_base.den;
    qDebug()<< "[formatCtx->duration]"
            << m_player_ctx->m_format_ctx->duration
            << QString("[%1 sec]").arg(m_player_ctx->m_format_ctx->duration / AV_TIME_BASE);
    qDebug()<< "[m_video_stream->start_time]"
            << m_player_ctx->m_video.stream->start_time
            ;
    qDebug()<< "[videoCtx->time_base]"
            << m_player_ctx->m_video.codec_ctx->time_base.num
            << m_player_ctx->m_video.codec_ctx->time_base.den
            << 1.0 * m_player_ctx->m_video.codec_ctx->time_base.num / m_player_ctx->m_video.codec_ctx->time_base.den
            ;
    qDebug()<< "[audioCtx->time_base]"
            << m_player_ctx->m_audio.codec_ctx->time_base.num
            << m_player_ctx->m_audio.codec_ctx->time_base.den
            << 1.0 * m_player_ctx->m_audio.codec_ctx->time_base.num / m_player_ctx->m_audio.codec_ctx->time_base.den
            ;
    qDebug()<< "[m_video_stream->avg_frame_rate]"
            << m_player_ctx->m_video.stream->avg_frame_rate.num
            << m_player_ctx->m_video.stream->avg_frame_rate.den
            << 1.0 * m_player_ctx->m_video.stream->avg_frame_rate.num / m_player_ctx->m_video.stream->avg_frame_rate.den
            ;
    qDebug()<< "[m_video_stream->r_frame_rate]"
            << m_player_ctx->m_video.stream->r_frame_rate.num
            << m_player_ctx->m_video.stream->r_frame_rate.den
            << 1.0 * m_player_ctx->m_video.stream->r_frame_rate.num / m_player_ctx->m_video.stream->r_frame_rate.den
            ;
    qDebug()<< "[m_video_stream->time_base]"
            << m_player_ctx->m_video.stream->time_base.num
            << m_player_ctx->m_video.stream->time_base.den
            << 1.0 * m_player_ctx->m_video.stream->time_base.num / m_player_ctx->m_video.stream->time_base.den
            ;
    qDebug()<< "[m_audio.stream->time_base]"
            << m_player_ctx->m_audio.stream->time_base.num
            << m_player_ctx->m_audio.stream->time_base.den
            << 1.0 * m_player_ctx->m_audio.stream->time_base.num / m_player_ctx->m_audio.stream->time_base.den
            ;

    //frameNo=0;
    m_drop_count = 0;


    this->setWindowTitle(filename);
    this->screenWidget->setScreenSize(QSize(m_player_ctx->m_video.codec_ctx->width, m_player_ctx->m_video.codec_ctx->height));
    this->screenWidget->adjustSize();

    if(false)
    {
        QMutexLocker(m_player_ctx->m_audio.packet_queue.mutex());
        QMutexLocker(m_player_ctx->m_video.packet_queue.mutex());
        while(m_player_ctx->enqueuePacket())
        {
            //qDebug() << m_player_ctx->m_audio.packet_queue.size() << m_player_ctx->m_video.packet_queue.size();

        }
    }

    //qDebug() << "[m_video.packet_queue.size()]" << m_player_ctx->m_video.packet_queue.size();
    //qDebug() << "[m_audio.packet_queue.size()]" << m_player_ctx->m_audio.packet_queue.size();

    m_packet_thread = new T_Packet_Thread(m_player_ctx);
    m_packet_thread->start();

    connect(&m_video_timer, SIGNAL(timeout()), SLOT(onTimerTimeout()));
    //m_video_timer.start(1);
    m_video_timer.start(5);
    //m_video_timer.start(10);

    m_generator = new T_Generator(m_player_ctx, 0);
    m_audioOutput = new QAudioOutput(m_device, m_format, this);
    m_audioOutput->start(m_generator);
    qDebug() << "[m_audioOutput->error()]" << m_audioOutput->error();
}

#if 0x1
void T_Packet_Thread::run()
{
    QTime midnight(0, 0, 0);
    qsrand(midnight.secsTo(QTime::currentTime()));
    const int c_enough = 12;
    const int c_wait_interval = 5000;
    const int c_wait_diff = 1000;
    const int c_sleep_interval = 1500;
    int v_wait_timing = c_wait_interval + (qrand() % c_wait_diff);
    for(;;)
    {
        if(m_player_ctx->m_audio.packet_queue.size() == 0)
        {
            qDebug() << "audio 0";
        }
        if(m_player_ctx->m_video.packet_queue.size() == 0)
        {
            qDebug() << "video 0";
        }
        while(m_player_ctx->m_audio.packet_queue.size() < c_enough
              ||
              m_player_ctx->m_video.packet_queue.size() < c_enough
              )
        {
            //qDebug() << "(1)";
            T_AV_Packet *v_av_packet = m_player_ctx->enqueuePacket();
            //qDebug() << "(2)";
            if(!v_av_packet) goto break1;
            //qDebug() << "(3)";
            //if(v_av_packet->timing() >= v_wait_timing)
            if(m_player_ctx->m_time_line.elapsed() >= v_wait_timing)
            {
                //qDebug() << "(4)";
                v_wait_timing += c_wait_interval + (qrand() % c_wait_diff);
                //qDebug() << "(before)" << v_wait_timing;
                //msleep(c_sleep_interval);
                qDebug() << "sleep";
                msleep(qrand() % c_sleep_interval);
                //qDebug() << "(after)" << v_wait_timing;
            }
            //qDebug() << "(5)";
        }
        //qDebug() << "(6)";
        msleep(1);
    }
    break1:;
}
#else
void T_Packet_Thread::run()
{
    T_AV_Packet *v_av_packet = NULL;
    while(v_av_packet = m_player_ctx->enqueuePacket())
    {
        //msleep(10);
        msleep(1);
    }
}
#endif

Widget::~Widget()
{
    qDebug() << "[Widget::~Widget()]";
    if(m_audioOutput) m_audioOutput->stop();
    m_video_timer.stop();
    delete ui;

    delete m_player_ctx;
    delete m_media_input;
}

void Widget::onTimerTimeout()
{
    QMutexLocker v_lock(&m_frame_mutex);
    Q_UNUSED(v_lock);
    //qDebug() << "[Widget::onTimerTimeout()]";
    if(m_player_ctx->m_video.packet_queue.size()==0)
    {
        // end of queue
        //qDebug() << "Widget::onTimerTimeout(1)";
        //m_video_timer.stop();
        m_player_ctx->m_time_line.stop();
        return;
    }
    m_player_ctx->m_time_line.start();
#if 0x0
    if(m_audioOutput->state()==QAudio::StoppedState)
    {
        m_audioOutput->start(m_generator); //FIXME
    }
#endif
    int v_elapsed = m_player_ctx->m_time_line.elapsed();
    //qDebug() << "[v_elapsed]" << v_elapsed << m_player_ctx->m_time_line.isActive();

    T_AV_Packet *v_head_packet = m_player_ctx->m_video.packet_queue.head();
    if(v_elapsed < v_head_packet->timing())
    {
        // too early
        ////qDebug() << "Widget::onTimerTimeout(2)" << v_elapsed << v_head_packet.m_av_packet.pts;
        return;
    }
    for(;;)
    {
        T_AV_Packet *v_last_packet = m_player_ctx->m_video.packet_queue.last();
        Q_ASSERT(v_last_packet);
        T_AV_Packet *v_curr_packet = m_player_ctx->m_video.packet_queue.dequeue();
        {
            if(m_player_ctx->m_video.packet_queue.size()==0)
            {
                //there is no next frame
                // should render current frame
                //qDebug() << "(1)       [frameNo]" << v_curr_packet->m_frame_no << "[packet.pts]" << v_curr_packet->timing();
                m_fps1.add(1);
                m_fps2.add(1);
            }
            else
            {
                T_AV_Packet *v_next_packet = m_player_ctx->m_video.packet_queue.head();
                if(v_elapsed >= v_next_packet->timing())
                {
                    // should render next frame
                    qDebug() << "=====>DROP[frameNo]" << v_curr_packet->m_frame_no << "[packet.pts]" << v_curr_packet->timing();
                    m_fps1.add(1);
                    m_drop_count++;
                    v_curr_packet->decodeVideo(NULL);
                    delete v_curr_packet;
                    v_curr_packet = NULL;
                    continue;
                }
                // should render current frame
                //qDebug() << "(2)       [frameNo]" << v_curr_packet->m_frame_no << "[packet.pts]" << v_curr_packet->timing();
                m_fps1.add(1);
                m_fps2.add(1);
            }
            {
                m_fps_download.set(v_last_packet->m_frame_no);
                QString v_msg = QString("%1:%2[%3 x %4] DL:%5 (%6)")
                        .arg(m_fps1.value(), 6, 'f', 2, ' ')
                        .arg(m_fps2.value(), 6, 'f', 2, ' ')
                        .arg(screenWidget->width())
                        .arg(screenWidget->height())
                        .arg(m_fps_download.value(), 6, 'f', 2, ' ')
                        .arg(v_last_packet->m_frame_no - v_curr_packet->m_frame_no)
                        ;
                this->ui->lineEdit->setText(v_msg);

                v_msg = QString("E=%1 F=%2/%3 D=%4")
                        .arg(m_player_ctx->m_time_line.toString("hh:mm:ss[zzz]"))
                        .arg(v_curr_packet->m_frame_no)
                        .arg(v_last_packet->m_frame_no)
                        .arg(m_drop_count)
                        ;
                this->ui->lineEdit_2->setText(v_msg);
            }
            {
                int v_ovl_w = screenWidget->width();
                int v_ovl_h = screenWidget->height();
                v_ovl_w = (v_ovl_w / 2) * 2;
                v_ovl_h = (v_ovl_h / 2) * 2;
                if(
                        v_ovl_w != screenWidget->screen->w
                        ||
                        v_ovl_h != screenWidget->screen->h
                        )
                {
                    Uint32 flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
                    screenWidget->screen = SDL_SetVideoModeEx(
                            v_ovl_w,
                            v_ovl_h,
                            0,
                            flags,
                            window_id);
                }
                SDL_Overlay *ovl = SDL_CreateYUVOverlay(
                        v_ovl_w,
                        v_ovl_h,
                        SDL_YV12_OVERLAY,
                        screenWidget->screen);
                v_curr_packet->decodeVideo(ovl);
                SDL_Rect rect;
                rect.x = 0;
                rect.y = 0;
                rect.w = ovl->w;
                rect.h = ovl->h;
                SDL_DisplayYUVOverlay(ovl, &rect);
                SDL_FreeYUVOverlay(ovl);
            }
        }
        delete v_curr_packet;
        v_curr_packet = NULL;
        break;
    }
}

